mod data;
mod expression;
mod program;
mod statement;
mod vm_writer;

use std::{
    fs,
    path::{Path, PathBuf},
};

use crate::{compiler::program::ProgramCompilerUnit, token::Tokenizer};
use anyhow::Result;
/// Compiler unit
pub(crate) struct Compiler;

impl Compiler {
    /// compile the jack file or path to vm file
    pub(crate) fn compile<P: AsRef<Path>>(jack_file: P) -> Result<Vec<PathBuf>> {
        let jack_file = jack_file.as_ref();
        let mut output_paths = Vec::new();
        if jack_file.is_dir() {
            // all the jack file path
            for file_path in jack_file.read_dir()?.flatten().filter_map(|item| {
                if item
                    .path()
                    .extension()
                    .unwrap_or_default()
                    .to_string_lossy()
                    == "jack"
                {
                    Some(item.path())
                } else {
                    None
                }
            }) {
                output_paths.push(Self::compile_file(&file_path)?);
            }
        } else {
            output_paths.push(Self::compile_file(jack_file)?);
        }
        Ok(output_paths)
    }
    /// compile the jack file to vm file
    fn compile_file(jack_file: &Path) -> Result<PathBuf> {
        let translator = Tokenizer::new(jack_file)?;
        let vm_code =
            ProgramCompilerUnit::compile_class(&mut translator.tokens()?.iter().peekable())?;
        fs::write(&translator.output_path_vm, &vm_code)?;
        Ok(translator.output_path_vm)
    }
}

#[cfg(test)]
mod test;
